﻿///<reference path="../../ViewModels/Interfaces/ICheckoutViewModel.ts"/>

module Enferno.Public.Web.Components.Controllers.Base {

    export interface IOnCheckoutInitilizedFunction {
        (checkoutController: CheckoutControllerBase<ViewModels.Interfaces.ICheckoutViewModel>): void;
    }

    export class CheckoutControllerBase<TChekoutViewModel extends ViewModels.Interfaces.ICheckoutViewModel> {
        
        public viewModel: TChekoutViewModel;
        private _accountController: AccountControllerBase;

        private _availablePaymentMethods: Array<Models.PaymentMethodModel>;
        private _availableDeliveryMethods: Array<Models.DeliveryMethodModel>;

        private _loggedInCustomer: Models.CustomerModel;
        private _basketId: string;
        private _hasSuccessfullyFetchedPersonInformationFromServer: boolean;
        private _paymentIdsToFetchInfoFromServerIsRequired: collections.Dictionary<number, boolean>;

        public basketController: BasketControllerBase<ViewModels.Interfaces.IBasketViewModel, ViewModels.Interfaces.IBasketItemViewModel>;

        public usePayerAsShipTo: boolean = true;
        public usePayerAsBuyer: boolean;
        private _requireSocialSecurityNumber: boolean;
        private _requireOrganisationNumber: boolean;

        public paymentErrorMessageHandler: (errorMessage: string) => string;
        public personCompanyDetailsFetchErrorMessageHandler: (errorMessage: string) => string;
        public generalErrorMessageHandler: (errorMessage: string) => string;

        constructor();
        constructor(basketController: BasketControllerBase<ViewModels.Interfaces.IBasketViewModel, ViewModels.Interfaces.IBasketItemViewModel>);
        constructor(basketController?: BasketControllerBase<ViewModels.Interfaces.IBasketViewModel, ViewModels.Interfaces.IBasketItemViewModel>) {
            this.basketController = basketController;
            this._accountController = new AccountControllerBase();
            this._paymentIdsToFetchInfoFromServerIsRequired = new collections.Dictionary<number, boolean>();
        }

        public static onInitializedFunctions = new Array<IOnCheckoutInitilizedFunction>();

        public init(): JQueryPromise<void> {
            var deferred = $.Deferred<void>();

            //fetch the checkout object (or create a new on if none exists)
            this.loadCheckout(true).then(() => {

                Enumerable.from(CheckoutControllerBase.onInitializedFunctions)
                    .reverse()
                    .forEach((fn: IOnCheckoutInitilizedFunction) => fn(this));

                this.updatePayerValuesAccordingToSelectedPaymentMethod(false);

                this.afterInitilized();

                deferred.resolve();
            });

            //when the basket change in some way, refetch the checkout object.
            $(document).on(BasketEvents.basketChanged, (eventObject: JQueryEventObject) => this.loadCheckout());
            $(document).on(AccountEvents.loggedIn, (eventObject: JQueryEventObject) => this._loggedInCustomer = eventObject.data);
            $(document).on(AccountEvents.loggedOut, (eventObject: JQueryEventObject) => this._loggedInCustomer = null);


            return deferred.promise();
        }

        public loadCheckout(isInitLoad: boolean = false): JQueryPromise<void> {
            var deferred = $.Deferred<void>();

            if (!isInitLoad) {
                this.viewModel.setIsLoading(true);
            }

            $.get(UrlHelper.getUrl(CheckoutServices.GetCheckout))
                .then(fetchedCheckout => {

                    this.setDataFromServer(fetchedCheckout).then(
                            checkoutModel => {
                                Enumerable.from(this._availablePaymentMethods)
                                    .forEach((paymentMethod: Models.PaymentMethodModel) => {
                                        this._paymentIdsToFetchInfoFromServerIsRequired.setValue(paymentMethod.id, false);
                                    });

                                var basketModel = new Models.BasketModel(fetchedCheckout.basket);
                                this._basketId = basketModel.id;

                                if (isInitLoad && this.basketController == null) { //only initialize the basket if this is the initial load and no basket controller has been givens.
                                    this.initBasketController(basketModel).then(() => deferred.resolve());
                                } else {
                                    deferred.resolve();
                                }
                            },
                            error => deferred.reject(error))
                        .always(() => {
                            if (!isInitLoad) {
                                this.viewModel.setIsLoading(false);
                            }
                        });
                });

            return deferred.promise();
        }

        public createViewModel(checkoutModel: Models.CheckoutModelBase<Models.ICheckoutPayerModel, Models.ICheckoutShipToModel>): TChekoutViewModel {
            throw "Class is abstract. Implement in concrete class";
        }

        public initBasketController(basketModel: Models.BasketModel): JQueryPromise<void> {
            throw "Class is abstract. Implement in concrete class";
        }

        public afterInitilized() {
            throw "Class is abstract. Implement in concrete class";
        }

        public setPaymentMethodAsFetchInfoFromServerIsRequired(paymentId: number) {
            if (!this._paymentIdsToFetchInfoFromServerIsRequired.containsKey(paymentId))
                throw "Cannot require server info fetching for non existing payment method";

            this._paymentIdsToFetchInfoFromServerIsRequired.setValue(paymentId, true);
        }

        public setPaymentMethod(paymentId: number, shouldBeSetAtServer: boolean = true): JQueryPromise<void> {
            var deferred = jQuery.Deferred<void>();
            this.viewModel.setIsLoading(true);
            if (shouldBeSetAtServer) {
                var url = UrlHelper.getUrl(CheckoutServices.SetPayment, [this._basketId]);
                $.post(url, { id: paymentId }).then(
                        updatedCheckout => {
                            this.setDataFromServer(updatedCheckout, true).then(
                                checkoutModel => {
                                    var shouldResetPayerInformation = this._paymentIdsToFetchInfoFromServerIsRequired.getValue(paymentId);
                                    this.updatePayerValuesAccordingToSelectedPaymentMethod(shouldResetPayerInformation);
                                    deferred.resolve();
                                },
                                error => deferred.reject(error));
                        },
                        error => {
                            deferred.reject(error);
                        })
                    .always(() => {
                        this.viewModel.setIsLoading(false);
                    });
            } else {
                this.viewModel.setSelectedPaymentMethod(paymentId);
                this.updatePayerValuesAccordingToSelectedPaymentMethod(false);
                deferred.resolve();
            }
            return deferred.promise();
        }

        public setDeliveryMethod(deliveryId: number, shouldBeSetAtServer: boolean = true): JQueryPromise<void> {
            var deferred = jQuery.Deferred<void>();
            this.viewModel.setIsLoading(true);
            if (shouldBeSetAtServer) {
                var url = UrlHelper.getUrl(CheckoutServices.SetDelivery, [this._basketId]);

                $.post(url, { id: deliveryId }).then(
                        updatedCheckout => {
                            this.setDataFromServer(updatedCheckout, true).then(
                                checkoutModel => {
                                    deferred.resolve();
                                },
                                error => deferred.reject(error));
                        },
                        error => {
                            deferred.reject(error);
                        })
                    .always(() => {
                        this.viewModel.setIsLoading(false);
                    });
            } else {
                this.viewModel.setSelectedDeliveryMethod(deliveryId);
                deferred.resolve();
            }


            return deferred.promise();
        }

        private setBuyerPersonName(firstName: string, lastName: string) {
            var model = <Models.PrivateCheckoutModel>this.viewModel.toModel();
            model.buyer.firstName = firstName;
            model.buyer.lastName = lastName;
            this.viewModel.update(model);
        }

        private updatePayerValuesAccordingToSelectedPaymentMethod(shouldResetPayerInformation: boolean = true) {
            var model = this.viewModel.toModel();
            var selectedPaymentMethod: Models.PaymentMethodModel = Enumerable.from(model.paymentMethods)
                .single((paymentMethod: Models.PaymentMethodModel) => paymentMethod.isSelected);
            if (this._paymentIdsToFetchInfoFromServerIsRequired.getValue(selectedPaymentMethod.id)) {
                if (shouldResetPayerInformation) {
                    this.clearPayerInformation();
                }

                this.viewModel.setIsEditable(false);
                this._requireOrganisationNumber = true;
                this._requireSocialSecurityNumber = true;
            } else {
                this.viewModel.setIsEditable(true);
                this._requireOrganisationNumber = false;
                this._requireSocialSecurityNumber = false;
            }           
        }

        private clearPayerInformation() {
            var checkoutModel = this.viewModel.toModel();
            var isInCompanyMode = this.viewModel.getIsCompanyMode();

            if (isInCompanyMode) {
                var companyModel = <Models.CompanyCheckoutModel>checkoutModel;
                companyModel.payer.companyCode = null;
                companyModel.payer.companyId = null;
                companyModel.payer.companyInformation = null;
                companyModel.payer.customerId = null;
                companyModel.payer.organisationNumber = null;
                checkoutModel = companyModel;
            } else {
                var privateModel = <Models.PrivateCheckoutModel>checkoutModel;
                privateModel.payer.personInformation = null;
                privateModel.payer.socialSecurityNumber = null;
                privateModel.payer.customerId = null;
                checkoutModel = privateModel;
            }

            checkoutModel.payer.invoiceAddress.careOf = null;
            checkoutModel.payer.invoiceAddress.line1 = null;
            checkoutModel.payer.invoiceAddress.line2 = null;
            checkoutModel.payer.invoiceAddress.city = null;
            checkoutModel.payer.invoiceAddress.zip = null;
            //note that we do not reset country here, we consider it to be something set from the server that should not be changed.

            this.viewModel.update(checkoutModel);
        }

        private setPayerInvoiceAddress(address: Models.AddressModel) {
            var model = this.viewModel.toModel();
            model.payer.invoiceAddress.careOf = address.careOf;
            model.payer.invoiceAddress.line1 = address.line1;
            model.payer.invoiceAddress.line2 = address.line2;
            model.payer.invoiceAddress.city = address.city;
            model.payer.invoiceAddress.zip = address.zip;
            this.viewModel.update(model);
        }

        private setPayerCompanyName(companyName: string) {
            var model = <Models.CompanyCheckoutModel>this.viewModel.toModel();
            model.payer.companyInformation.name = companyName;
            this.viewModel.update(model);
        }

        private setPayerPersonName(firstName: string, lastName: string) {
            var model = <Models.PrivateCheckoutModel>this.viewModel.toModel();
            model.payer.personInformation.firstName = firstName;
            model.payer.personInformation.lastName = lastName;
            this.viewModel.update(model);
        }

        private changeMode(isCompanyMode: boolean, shouldBeSetAtServer: boolean = true): JQueryPromise<void> {
            var deferred = jQuery.Deferred<void>();

            //clear error messages
            this.viewModel.clearGeneralErrorMessage();
            this.viewModel.clearPaymentErrorMessage();
            this.viewModel.clearPersonCompanyDetailsFetchErrorMessage();

            this.viewModel.setIsCompanyMode(isCompanyMode);
            var model = this.viewModel.toModel();

            // Payer.ShipToCompany
            if (shouldBeSetAtServer) {
                var url = UrlHelper.getUrl(CheckoutServices.UpdatePrivateInformation, [this._basketId]);
                if (isCompanyMode)
                    url = UrlHelper.getUrl(CheckoutServices.UpdateCompanyInformation, [this._basketId]);
                $.ajax({
                    type: 'POST',
                    url: url,
                    contentType: 'application/json; charset=utf-8',
                    dataType: 'json',
                    data: JSON.stringify(model.serialize())
                }).then(
                    updatedCheckout => {
                        this.setDataFromServer(updatedCheckout, false).then(
                            checkoutModel => {
                                deferred.resolve();
                            },
                            error => deferred.reject(error));
                    },
                    error => {
                        deferred.reject(error);
                    })
                    .always(() => {
                        this.viewModel.setIsLoading(false);
                    });
            }
            //deliveryMethods
            var validDeliveryMethods = this.getValidDeliveryMethods();

            var currentlySelectedDeliveryMethod: Models.DeliveryMethodModel = Enumerable.from(model.deliveryMethods)
                .singleOrDefault((deliveryMethod: Models.DeliveryMethodModel) => deliveryMethod.isSelected);

            var selectedDeliverMethodIsValid = Enumerable.from(validDeliveryMethods)
                .any((validDeliveryMethod: Models.DeliveryMethodModel) => validDeliveryMethod.id == currentlySelectedDeliveryMethod.id);

            var setDeliveryDeferred = jQuery.Deferred<void>();
            var setDeliveryPromise = setDeliveryDeferred.promise();
            if (!selectedDeliverMethodIsValid) { //the currently selected deliverMethod is not a valid delivery method, set to the first valid delivery method
                setDeliveryPromise = this.setDeliveryMethod(model.deliveryMethods[0].id, shouldBeSetAtServer);
            } else {
                setDeliveryDeferred.resolve();
            }

            //paymentMethods
            var validPaymentMethods = this.getValidPaymentMethods();

            var currentlySelectedPaymentMethod: Models.PaymentMethodModel = Enumerable.from(model.paymentMethods)
                .singleOrDefault((paymentMethod: Models.PaymentMethodModel) => paymentMethod.isSelected);

            var selectedPaymentMethodIsValid = Enumerable.from(validPaymentMethods)
                .any((validPaymentMethod: Models.PaymentMethodModel) => validPaymentMethod.id == currentlySelectedPaymentMethod.id);

            var setPaymentDeferred = jQuery.Deferred<void>();
            var setPaymentPromise = setPaymentDeferred.promise();
            if (!selectedPaymentMethodIsValid) { //the currently selected paymentMethod is not a valid payment method, set to the first valid payment method
                setPaymentPromise = this.setPaymentMethod(model.paymentMethods[0].id, shouldBeSetAtServer);
            } else {
                setPaymentDeferred.resolve();
            }

            $.when(setDeliveryPromise, setPaymentPromise)
                .then((result1) => {
                    model = this.viewModel.toModel();
                    model.deliveryMethods = validDeliveryMethods;
                    model.paymentMethods = validPaymentMethods;
                    this.viewModel.update(model);
                    deferred.resolve();
                });

            return deferred.promise();
        }

        public changeToPrivateMode(): JQueryPromise<void> {
            return this.changeMode(false);
        }

        public changeToCompanyMode(): JQueryPromise<void> {
            return this.changeMode(true);
        }

        public updateAddressAndNameInformation(): JQueryPromise<void> {
            var deferred = jQuery.Deferred<void>();
            this.viewModel.clearPersonCompanyDetailsFetchErrorMessage();
            if (this.viewModel.getIsCompanyMode()) {
                var orgNo = this.viewModel.getOrganisationNumber();
                this.viewModel.setIsLoading(true);
                this._accountController.getCompanyInfo(orgNo).then(companyInfo => {
                        this.setPayerInvoiceAddress(companyInfo.address);
                        this.setPayerCompanyName(companyInfo.companyInformation.name);
                    })
                    .always(() => {
                        this.viewModel.setIsLoading(false);
                    });
            } else {
                var ssn = this.viewModel.getSocialSecurityNumber();
                this.viewModel.setIsLoading(true);
                this._accountController.getPersonInfo(ssn).then(
                        personInfo => {
                            this.setBuyerPersonName(personInfo.personInformation.firstName, personInfo.personInformation.lastName);
                            this.setPayerInvoiceAddress(personInfo.address);
                            this.setPayerPersonName(personInfo.personInformation.firstName, personInfo.personInformation.lastName);
                            deferred.resolve();
                        }, error => {
                            this.setPersonCompanyDetailsFetchErrorMessage(error);
                            deferred.reject(error);
                        })
                    .always(() => {
                        this.viewModel.setIsLoading(false);
                    });
            }
            return deferred.promise();
        }

        public purchase(): JQueryPromise<void> {
            var deferred = jQuery.Deferred<void>();

            this.viewModel.clearPaymentErrorMessage();

            var url = UrlHelper.getUrl(CheckoutServices.PrivatePurchase, [this._basketId]);
            var isPrivateCheckout = true;

            if (this.viewModel.getIsCompanyMode()) {
                url = UrlHelper.getUrl(CheckoutServices.CompanyPurchase, [this._basketId]);
                isPrivateCheckout = false;
            }

            var model = this.viewModel.toModel();
            this.ensureBuyerInformation(model, isPrivateCheckout);
            this.ensureShipToInformation(model, isPrivateCheckout);
            try {
                this.validate(model, isPrivateCheckout);
            } catch (exception) {
                this.setPaymentErrorMessage(exception);
                deferred.reject(exception);
                return; //the purchase information did not validate, abort here.
            }

            var serializedModel = model.serialize();
            this.viewModel.setIsLoading(true);
            $.ajax({
                    type: 'POST',
                    url: url,
                    contentType: 'application/json; charset=utf-8',
                    dataType: 'json',
                    data: JSON.stringify(serializedModel)
                }).then(
                    paymentResponse => {
                        var paymentResponseModel = new Models.PaymentResponseModel(paymentResponse);
                        if (paymentResponseModel.status == Models.PaymentStatus.Ok) {
                            if (paymentResponseModel.redirectUrl != null) { //we have a payment response redirect url, we should redirect
                                if (paymentResponseModel.httpMethod == "post") { //do a post redirect (DIBS works this way)
                                    deferred.resolve();
                                    UrlHelper.redirect(paymentResponseModel.redirectUrl, paymentResponseModel.redirectParameters, paymentResponseModel.httpMethod);
                                } else { //do a regular get redirect
                                    deferred.resolve();
                                    window.location.href = paymentResponseModel.redirectUrl;
                                }

                            } else { //no payment response redirect url was given, just do the redirect to the success page directly

                                deferred.resolve();
                                window.location.href = paymentResponseModel.successUrl;

                            }
                        } else { //something went wrong with the payment, reject with the status description as error.
                            this.setPaymentErrorMessage(paymentResponseModel.statusDescription);
                            deferred.resolve();
                        }
                    },
                    error => {
                        this.setPaymentErrorMessage(error.statusText);
                        deferred.reject(error);
                    })
                .always(() => {
                    this.viewModel.setIsLoading(false);
                });

            return deferred.promise();
        }

        private setPaymentErrorMessage(errorMessage: string) {
            if (this.paymentErrorMessageHandler != null) {
                errorMessage = this.paymentErrorMessageHandler(errorMessage);
            }
            this.viewModel.setPaymentErrorMessage(errorMessage);
        }

        private setPersonCompanyDetailsFetchErrorMessage(errorMessage: string) {
            if (this.personCompanyDetailsFetchErrorMessageHandler != null) {
                errorMessage = this.personCompanyDetailsFetchErrorMessageHandler(errorMessage);
            }
            this.viewModel.setPersonCompanyDetailsFetchErrorMessage(errorMessage);
        }

        private setGeneralErrorMessage(errorMessage: string) {
            if (this.generalErrorMessageHandler != null) {
                errorMessage = this.generalErrorMessageHandler(errorMessage);
            }
            this.viewModel.setGeneralErrorMessage(errorMessage);
        }

        private validate(model: Models.CheckoutModelBase<Models.ICheckoutPayerModel, Models.ICheckoutShipToModel>, isPrivate: boolean) {
            if (model.buyer.isEmpty())
                throw "Buyer information cannot be empty";

            if (isPrivate) {
                var privatePayer = <Models.CheckoutPrivatePayerModel>model.payer;
                if (privatePayer.personInformation.isEmpty()) {
                    throw "Private payer needs person information";
                }
                if (this._requireSocialSecurityNumber && (privatePayer.socialSecurityNumber == null || privatePayer.socialSecurityNumber == "")) {
                    throw "Social security number is required for payer";
                }
                if (!privatePayer.invoiceAddress.isValid()) {
                    throw "Invoice address is invalid";
                }

                var privateShipTo = <Models.CheckoutPrivateShipToModel>model.shipTo;
                if (privateShipTo.personInformation.isEmpty()) {
                    throw "Private shipTo needs person information";
                }
                if (this._requireSocialSecurityNumber && (privateShipTo.socialSecurityNumber == null || privateShipTo.socialSecurityNumber == "")) {
                    throw "Social security number is required for shipTo";
                }
                if (!privateShipTo.deliveryAddress.isValid()) {
                    throw "Delivery address is invalid";
                }

            } else {
                var companyPayer = <Models.CheckoutCompanyPayerModel>model.payer;
                if (companyPayer.companyInformation.isEmpty()) {
                    throw "Company payer needs company information";
                }
                if (this._requireOrganisationNumber && (companyPayer.organisationNumber == null || companyPayer.organisationNumber == "")) {
                    throw "Organisation number is required for payer";
                }
                if (!companyPayer.invoiceAddress.isValid()) {
                    throw "Invoice address is invalid";
                }

                var companyShipTo = <Models.CheckoutCompanyShipToModel>model.shipTo;
                if (companyShipTo.companyInformation.isEmpty()) {
                    throw "Company shipTo needs company information";
                }
                if (this._requireSocialSecurityNumber && (companyShipTo.organisationNumber == null || companyShipTo.organisationNumber == "")) {
                    throw "Organisation number is required for shipTo";
                }
                if (!companyShipTo.deliveryAddress.isValid()) {
                    throw "Delivery address is invalid";
                }
            }
        }

        private ensureBuyerInformation(model: Models.CheckoutModelBase<Models.ICheckoutPayerModel, Models.ICheckoutShipToModel>, isPrivate: boolean) {
            if (isPrivate) {
                var privatePayer = <Models.CheckoutPrivatePayerModel>model.payer;
                if (this.usePayerAsBuyer || model.buyer.isEmpty()) {
                    model.buyer.firstName = privatePayer.personInformation.firstName;
                    model.buyer.lastName = privatePayer.personInformation.lastName;
                    model.buyer.email = privatePayer.personInformation.email;
                    model.buyer.phone = privatePayer.personInformation.phone;
                    model.buyer.cellPhone = privatePayer.personInformation.cellPhone;
                }
            } else {
                var companyPayer = <Models.CheckoutCompanyPayerModel>model.payer;
                if (companyPayer.invoiceAddress.careOf == null || companyPayer.invoiceAddress.careOf == "") {
                    companyPayer.invoiceAddress.careOf = model.buyer.firstName + ' ' + model.buyer.lastName;
                }
            }
        }

        private ensureShipToInformation(model: Models.CheckoutModelBase<Models.ICheckoutPayerModel, Models.ICheckoutShipToModel>, isPrivate: boolean) {
            if (isPrivate) {
                var privateShipTo = <Models.CheckoutPrivateShipToModel>model.shipTo;
                var privatePayer = <Models.CheckoutPrivatePayerModel>model.payer;
                if (this.usePayerAsShipTo) {
                    privateShipTo.personInformation = privatePayer.personInformation;
                    privateShipTo.customerId = privatePayer.customerId;
                    privateShipTo.deliveryAddress = privatePayer.invoiceAddress;
                    privateShipTo.socialSecurityNumber = privatePayer.socialSecurityNumber;
                } else {
                    if (privateShipTo.personInformation.isEmpty()) {
                        privateShipTo.personInformation = privatePayer.personInformation;
                    }
                }
            } else {
                if (this.usePayerAsShipTo) {
                    var companyPayer = <Models.CheckoutCompanyPayerModel>model.payer;
                    var companyShipTo = <Models.CheckoutCompanyShipToModel>model.shipTo;
                    companyShipTo.companyId = companyPayer.companyId;
                    companyShipTo.companyInformation = companyPayer.companyInformation;
                    companyShipTo.customerId = companyPayer.customerId;
                    companyShipTo.deliveryAddress = companyPayer.invoiceAddress;
                    companyShipTo.organisationNumber = companyPayer.organisationNumber;
                }
            }

        }

        private setDataFromServer(serverObject: any, setOnlyDeliveryAndPaymentMethods: boolean = false): JQueryPromise<Models.CheckoutModelBase<Models.ICheckoutPayerModel, Models.ICheckoutShipToModel>> {

            var deferred = jQuery.Deferred<Models.CheckoutModelBase<Models.ICheckoutPayerModel, Models.ICheckoutShipToModel>>();

            if (serverObject.typeName == "PrivateCheckoutModel") {
                var privateCheckoutModel = new Models.PrivateCheckoutModel(serverObject);

                this._availableDeliveryMethods = privateCheckoutModel.deliveryMethods;
                this._availablePaymentMethods = privateCheckoutModel.paymentMethods;

                if (this.viewModel != null) {
                    if (setOnlyDeliveryAndPaymentMethods) {
                        this.viewModel.updateDeliveryMethods(privateCheckoutModel.deliveryMethods);
                        this.viewModel.updatePaymentMethods(privateCheckoutModel.paymentMethods);
                    } else {
                        this.viewModel.update(privateCheckoutModel);
                    }
                } else {
                    this.viewModel = this.createViewModel(privateCheckoutModel);
                }

                this.changeMode(false, false).then(
                    () => deferred.resolve(this.viewModel.toModel()),
                    error => deferred.reject(error));

            } else if (serverObject.typeName == "CompanyCheckoutModel") {
                var companyCheckoutModel = new Models.CompanyCheckoutModel(serverObject);

                this._availableDeliveryMethods = companyCheckoutModel.deliveryMethods;
                this._availablePaymentMethods = companyCheckoutModel.paymentMethods;

                if (this.viewModel != null) {
                    if (setOnlyDeliveryAndPaymentMethods) {
                        this.viewModel.updateDeliveryMethods(companyCheckoutModel.deliveryMethods);
                        this.viewModel.updatePaymentMethods(companyCheckoutModel.paymentMethods);
                    } else {
                        this.viewModel.update(companyCheckoutModel);
                    }
                } else {
                    this.viewModel = this.createViewModel(companyCheckoutModel);
                }

                this.changeMode(true, false).then(
                    () => deferred.resolve(this.viewModel.toModel()),
                    error => deferred.reject(error));
            } else {
                var message = "Can not create checkout object from server object with type " + serverObject.typeName;
                deferred.reject(message);
            }

            return deferred.promise();
        }

        public getValidDeliveryMethods(): Array<Models.DeliveryMethodModel> {
            var isCompanyMode = this.viewModel.getIsCompanyMode();
            if (isCompanyMode) {
                return Enumerable.from(this._availableDeliveryMethods)
                    .where((deliveryMethod: Models.DeliveryMethodModel) => {
                        return !deliveryMethod.isForPersonOnly;
                    }).toArray();
            } else {
                return Enumerable.from(this._availableDeliveryMethods)
                    .where((deliveryMethod: Models.DeliveryMethodModel) => {
                        return !deliveryMethod.isForCompanyOnly;
                    }).toArray();
            }

        }

        public getValidPaymentMethods(): Array<Models.PaymentMethodModel> {
            var isCompanyMode = this.viewModel.getIsCompanyMode();
            if (isCompanyMode) {
                return Enumerable.from(this._availablePaymentMethods)
                    .where((paymentMethod: Models.PaymentMethodModel) => {
                        return !paymentMethod.isForPersonOnly;
                    }).toArray();
            } else {
                return Enumerable.from(this._availablePaymentMethods)
                    .where((paymentMethod: Models.PaymentMethodModel) => {
                        return !paymentMethod.isForCompanyOnly;
                    }).toArray();
            }
        }

    }

} 